package com.leo.boot.util;

import com.leo.boot.feature.Dimension;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;

import static cn.hutool.core.bean.BeanUtil.copyProperties;
import static cn.hutool.core.bean.BeanUtil.getProperty;
import static cn.hutool.core.collection.CollUtil.getFirst;
import static cn.hutool.core.collection.CollUtil.isEmpty;
import static cn.hutool.core.collection.CollUtil.size;
import static cn.hutool.core.text.CharSequenceUtil.isBlank;
import static cn.hutool.core.util.ArrayUtil.isEmpty;
import static cn.hutool.core.util.ArrayUtil.sub;
import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toList;

@Slf4j
@UtilityClass
public class DimensionUtils {

    public static final String UNDEFINED = "未定义";

    public static <T extends Dimension<T>> List<T> groupBy(List<T> instances, String... classifiers) {
        if (isEmpty(instances) || isEmpty(classifiers)) {
            return instances;
        }
        String currentField = classifiers[0];
        String[] subFields = sub(classifiers, 1, classifiers.length);

        List<T> result = new ArrayList<>();
        instances.stream().collect(groupingBy(instance -> getDimension(instance, currentField), LinkedHashMap::new, toList()))
                .forEach((dimension, children) -> {
                    if (isEmpty(subFields)) {
                        T summing = summing(dimension, children);
                        result.add(summing);
                        return;
                    }
                    if (!UNDEFINED.equals(dimension)) {
                        children = groupBy(children, subFields);
                    }
                    if (size(children) == 1 && dimension.equals(getFirst(children).getDimension())) {
                        children = getFirst(children).getChildren();
                    }
                    T summing = summing(dimension, children);
                    result.add(summing);
                });
        return result;
    }

    public static <T extends Dimension<T>> T summing(String dimension, List<T> instances) {
        T summing = instances.stream().reduce(Dimension::reduce).map(DimensionUtils::copy).orElse(null);
        if (nonNull(summing)) {
            summing.setDimension(dimension);
            summing.setChildren(instances);
        }
        return summing;
    }

    static String getDimension(Object instance, String field) {
        Object value = getProperty(instance, field);
        return isNull(value) || isBlank(value.toString()) ? UNDEFINED : value.toString();
    }

    @SuppressWarnings("unchecked")
    static <T extends Dimension<T>> T copy(T instance) {
        return (T) copyProperties(instance, instance.getClass());
    }
}